<?php

namespace App\Helper;

use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Guzzle\ClientFactory;
use Hyperf\Utils\Arr;
use Hyperf\Utils\Coroutine;
use Psr\Container\ContainerInterface;
use Psr\SimpleCache\CacheInterface;

/**
 * 企业微信消息通知者
 * @package App\Helper\Notifier
 * @example
 * 1. 发送文本消息[默认配置：config('weixin.work.default')]
 * (new Notifier)->sendText('文本消息')
 *
 * 2. 发送文本消息[默认配置：config('weixin.work.notice')]
 * (new Notifier)->driver('default')->sendText('文本消息')
 * 2. 发送文本消息（动态配置）
 * (new Notifier)->setWorkId('work_id')->setAgentId('agent_id')->setSecret('secret')->sendText('文本消息')
 */
class Notifier
{
    /**
     * @var ContainerInterface
     */
    private $container;

    private $configPrefix = 'weixin.work';

    /**
     * @var string
     */
    private $agentId;

    /**
     * @var string
     */
    private $workId;

    /**
     * @var string
     */
    private $secret;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * @param string $value
     * @return $this
     */
    public function driver($value = 'default')
    {
        $this->configPrefix .= '.' . $value;
        $this->initDefaultConfig();
        return $this;
    }

    /**
     * @param $value
     * @return Notifier
     */
    public function setWorkId($value)
    {
        $this->workId = $value;
        return $this;
    }

    /**
     * @param $value
     * @return Notifier
     */
    public function setAgentId($value)
    {
        $this->agentId = $value;
        return $this;
    }

    /**
     * @param $value
     * @return Notifier
     */
    public function setSecret($value)
    {
        $this->secret = $value;
        return $this;
    }

    /**
     * 发送文件消息
     * @param $message
     */
    public function sendText($message)
    {
        // 1. 检测配置
        $this->checkConfig();
        // 2. 发送消息
        $this->send(function () use ($message) {
            $payload = [
                'json' => [
                    'touser' => '@all',
                    'msgtype' => 'text',
                    'agentid' => $this->agentId,
                    'text' => ['content' => $message]
                ]
            ];
            $this->request()->post($this->getSendApi(), $payload);
        });
    }

    /**
     * 发送前检测配置
     */
    private function checkConfig()
    {
        if (!$this->workId || !$this->agentId || !$this->secret) {
            $this->driver();
        }
    }

    protected function send(callable $fn)
    {
        if (Coroutine::inCoroutine()) {
            go($fn);
        } else {
            call_user_func($fn);
        }
    }

    private function request()
    {
        return $this->container->get(ClientFactory::class)->create();
    }

    protected function initDefaultConfig()
    {
        $defaultConfig = $this->container->get(ConfigInterface::class)->get($this->configPrefix);
        $this->setWorkId(Arr::get($defaultConfig, 'work_id'))
            ->setAgentId(Arr::get($defaultConfig, 'agent_id'))
            ->setSecret(Arr::get($defaultConfig, 'secret'));
    }

    private function getSendApi()
    {
        return sprintf('https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s', $this->getAccessToken());
    }

    private function getTokenApi($id, $secret)
    {
        return sprintf('https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s', $id, $secret);
    }

    private function getCacheKey()
    {
        return self::class . $this->configPrefix . ':access_token';
    }

    /**
     * @return string
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    protected function getAccessToken()
    {
        $token = $this->container->get(CacheInterface::class)->get($this->getCacheKey());
        if (!$token) {
            $result = json_decode(file_get_contents($this->getTokenApi($this->workId, $this->secret)), true);
            if (Arr::get($result, 'errcode') == 0) {
                $token = Arr::get($result, 'access_token');
                $this->container->get(CacheInterface::class)->set($this->getCacheKey(), $token, Arr::get($result, 'expires_in'));
            } else {
                $this->container->get(StdoutLoggerInterface::class)->error('获取企业微信 access_token 获取失败', $result);
                throw new \RuntimeException('获取企业微信 access_token 获取失败');
            }
        }
        return $token;
    }
}